

var TAG_SPRITE_MANAGER = 1;
var PTM_RATIO = 32.0;

/* b2 Var shortcut */
var b2Vec2 = Box2D.Common.Math.b2Vec2
        , b2BodyDef = Box2D.Dynamics.b2BodyDef
        , b2Body = Box2D.Dynamics.b2Body
        , b2FixtureDef = Box2D.Dynamics.b2FixtureDef
        , b2World = Box2D.Dynamics.b2World
        , b2ContactListener = Box2D.Dynamics.b2ContactListener
        , b2AABB = Box2D.Collision.b2AABB
        , b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
        , b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;



var b2DebugDrawLayer = cc.Node.extend({
    
    b2World:null,
    
    ctor:function(_world){
        this._super();
        
        this.b2World = _world;
        /*
        Box2D.Dynamics.b2DebugDraw.e_shapeBit = 0x0001;
        Box2D.Dynamics.b2DebugDraw.e_jointBit = 0x0002;
        Box2D.Dynamics.b2DebugDraw.e_aabbBit = 0x0004;
        Box2D.Dynamics.b2DebugDraw.e_pairBit = 0x0008;
        Box2D.Dynamics.b2DebugDraw.e_centerOfMassBit = 0x0010;
        Box2D.Dynamics.b2DebugDraw.e_controllerBit = 0x0020;
        */
        
        this.b2World.SetDebugDraw( new Box2D.Dynamics.b2DebugDraw() );
        this.b2World.m_debugDraw.AppendFlags( Box2D.Dynamics.b2DebugDraw.e_shapeBit );
        this.b2World.m_debugDraw.m_drawScale = PTM_RATIO;
        this.b2World.m_debugDraw.SetAlpha(0.6);
        this.b2World.m_debugDraw.SetFillAlpha(0.6);
        
        this.b2World.m_debugDraw.SetSprite( cc.renderContext );
        
        var size = cc.Director.getInstance().getWinSize();
        //this.setPosition( cc.p(size.width / 2, size.height / 2) );
        this.setScaleY( -1 );
    },
    
    draw:function (ctx) {
        this.b2World.DrawDebugData();
    },
});


var TagList = {
  Common :      {value: 0, name: "Common"}, 
  Box:          {value: 1, name: "Box"}, 
  WaterSensor : {value: 2, name: "WaterSensor"},
  Circle :      {value: 3, name: "Circle"},
};

function b2AddOn (cocosData) {
    this.cocosData = cocosData;
    this.selectable = false;
    this.tag = TagList.Common;
};


function transformToB2World(cocosWorldP){
    var worldP = cocosWorldP;
    worldP = cc.Director.getInstance().convertToGL(worldP);
    var b2WorldPos = new b2Vec2( worldP.x / PTM_RATIO, worldP.y / PTM_RATIO );
    
    return b2WorldPos;
};

var _waterScene = cc.Layer.extend({
   
    _water:null,
    world:null,
    boxBatchNode:null,
    SelectedObject:null,
    b2MousePosition:null,
    waterSensorAABB:null,
    BoxInWaterList:[],
    
    init:function () {
        var selfPointer = this;
        //////////////////////////////
        // 1. super init first
        this._super();

        this.setTouchEnabled(true);
        this.setKeyboardEnabled(true);
        
        
        
        var size = cc.Director.getInstance().getWinSize();
        
        
        // physic
        this.world = new b2World(new b2Vec2(0, -10), false);
        this.world.SetContinuousPhysics(true);
        
        
        // physic debug draw
        var Debug = new b2DebugDrawLayer( this.world );
        Debug.setVisible(false);
        this.addChild( Debug , 255 );
        
        
        // contact response
        this.initContactListener();

        // world limit 
        this.initWorldBound();
        
        
        // background

        var background = cc.Sprite.create("res/back_waterScene.png");
        background.setPosition(cc.p(size.width / 2, size.height / 2));
        background.setVisible(true);
        background.setAnchorPoint(cc.p(0.5, 0.5));
        this.addChild(background, 0);

    
        //Menu (back button)
        var items = new Array();
        var p = cc.p( (-size.width/2) + 20 ,
                      (-size.height/2) + 30 );
        items[0] = createButton("Back");
        items[0].setPosition( p );
        items[0].setCallback(this,
            function () {
                myApp.GoToScene( new HelloWorldScene() );
            });
        var myMenu = cc.MenuCustom.create(items);
        this.addChild(myMenu);
        
        
        // glass
        var __this = this;
        this.initGlass( function(){
            
            // in the middle of the glass
        
            //Set up Box sprite
            var mgr = cc.SpriteBatchNode.create("res/Box.png", 10);
            __this.addChild(mgr);
            __this.boxBatchNode = mgr;
            __this.addNewSpriteWithCoords(cc.p(50,50));
            __this.addNewSpriteWithCoords(cc.p(80,200));
            __this.addNewSpriteWithCoords(cc.p(150,300));
            __this.addNewSpriteWithCoords(cc.p(40,450));
            __this.addNewSpriteWithCoords(cc.p(200,100));
            __this.addNewSpriteWithCoords(cc.p(180,380));
        });

        
        this.scheduleUpdate();
        
        return true;
    },
    
    addNewSpriteWithCoords:function (p) {
    
        var batch = this.boxBatchNode;

        var CCSize = 50;
        var b2Size = CCSize / PTM_RATIO;
        
        var sprite = cc.Sprite.createWithTexture(batch.getTexture(), cc.rect(0, 0, CCSize, CCSize));
        batch.addChild(sprite);
        sprite.setPosition(cc.p(p.x, p.y));

        // Define the dynamic body.
        //Set up a 1m squared box in the physics world
        var b2BodyDef = Box2D.Dynamics.b2BodyDef
            , b2Body = Box2D.Dynamics.b2Body
            , b2FixtureDef = Box2D.Dynamics.b2FixtureDef
            , b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape;

        var bodyDef = new b2BodyDef();
        bodyDef.type = b2Body.b2_dynamicBody;
        bodyDef.position.Set(p.x / PTM_RATIO, p.y / PTM_RATIO);
        bodyDef.userData = new b2AddOn(sprite);
        bodyDef.userData.selectable = true;
        bodyDef.userData.tag = TagList.Box;
        var body = this.world.CreateBody(bodyDef);

        // Define another box shape for our dynamic body.
        var dynamicBox = new b2PolygonShape();
        dynamicBox.SetAsBox(b2Size/2.0, b2Size/2.0);//These are mid points for our 'b2Size' meter box

        // Define the dynamic body fixture.
        var fixtureDef = new b2FixtureDef();
        fixtureDef.shape = dynamicBox;
        fixtureDef.density = 1.0;
        fixtureDef.friction = 0.3;
        body.CreateFixture(fixtureDef);
    },
    
    // init function
    initContactListener:function(){
        
                var __this = this;
        var myContactListener = new b2ContactListener();
        myContactListener.BeginContact = function(contact){
            
            var b2AddOnA = contact.m_fixtureA.GetBody().GetUserData();
            var b2AddOnB = contact.m_fixtureB.GetBody().GetUserData();
            
            if( b2AddOnA.tag == TagList.Box &&
                b2AddOnB.tag == TagList.WaterSensor )
            {
                var contact_point = b2AddOnA.cocosData.getPosition();
                
                __this._water.Height += 10;
                __this._water.addForce(contact_point,-200);
            } 
        };
        myContactListener.EndContact = function(contact){
        
            var b2AddOnA = contact.m_fixtureA.GetBody().GetUserData();
            var b2AddOnB = contact.m_fixtureB.GetBody().GetUserData();
            
            if( b2AddOnA.tag == TagList.Box &&
                b2AddOnB.tag == TagList.WaterSensor )
            {
                var contact_point = b2AddOnA.cocosData.getPosition();
                
                __this._water.Height -= 10;
                __this._water.addForce(contact_point,200);
            }
        };
        this.world.SetContactListener(myContactListener);
    },
    initWorldBound:function(){
        var size = cc.Director.getInstance().getWinSize();
        
        var fixDef = new b2FixtureDef;
        fixDef.density = 1.0;
        fixDef.friction = 0.5;
        fixDef.restitution = 0.2;

        var bodyDef = new b2BodyDef;
        //create ground
        bodyDef.type = b2Body.b2_staticBody;
        fixDef.shape = new b2PolygonShape;
        fixDef.shape.SetAsBox(20, 2);
        // upper
        bodyDef.position.Set(10, size.height / PTM_RATIO + 1.8);
        bodyDef.userData = new b2AddOn();
        this.world.CreateBody(bodyDef).CreateFixture(fixDef);
        // bottom
        bodyDef.position.Set(10, -1.8);
        bodyDef.userData = new b2AddOn();
        this.world.CreateBody(bodyDef).CreateFixture(fixDef);
        fixDef.shape.SetAsBox(2, 14);
        // left
        bodyDef.position.Set(-1.8, 13);
        bodyDef.userData = new b2AddOn();
        this.world.CreateBody(bodyDef).CreateFixture(fixDef);
        // right
        bodyDef.position.Set(26.8, 13);
        bodyDef.userData = new b2AddOn();
        this.world.CreateBody(bodyDef).CreateFixture(fixDef);
    },
    initGlass:function( MiddleLayerCallback ){
        
        var size = cc.Director.getInstance().getWinSize();
        // glass def
        var glassX = 560.0;
        var glassY = -1.0;
        var glassW = 231.0;
        var glassH = 243.0;
        var glassBW = 231.0;
        var glassBH = 10.0;
        var glassLW = 17.0;
        var glassLH = 222.0;
        var glassRW = 17.0;
        var glassRH = 222.0;
        var glassRXOffset = 214.0;
        var glassSensorH = 100.0;
        
        
        // glass background
        var glass = cc.CustomSprite.create("res/glass_b.png");
        glass.setPosition(cc.p(glassX,glassY));
        this.addChild(glass);
        
        
        
        // callback object between background and front
        if( MiddleLayerCallback )
            MiddleLayerCallback();
        
        
        
        // water
        var water = new Water(glassW-4,100,12);
        water.setPosition(cc.p(glassX+2,glassY+3));
        this.addChild(water);
        this._water = water;
        
        
        // glass foreground
        var glass = cc.CustomSprite.create("res/glass.png");
        glass.setPosition(cc.p(glassX,glassY));
        this.addChild(glass);
            // glass physic
            var glassfixDef = new b2FixtureDef;
            glassfixDef.density = 1.0;
            glassfixDef.friction = 0.5;
            glassfixDef.restitution = 0.2;

            var glassBodyDef = new b2BodyDef;
            glassBodyDef.type = b2Body.b2_staticBody;
            // Bottom
            glassfixDef.shape = new b2PolygonShape;
            glassfixDef.shape.SetAsBox( (glassBW/2)/PTM_RATIO, (glassBH/2)/PTM_RATIO );
            glassBodyDef.position.Set( (glassX+(glassBW/2))/PTM_RATIO, (glassY+(glassBH/2))/PTM_RATIO);
            glassBodyDef.userData = new b2AddOn();
            this.world.CreateBody(glassBodyDef).CreateFixture(glassfixDef);
            // left
            glassfixDef.shape = new b2PolygonShape;
            glassfixDef.shape.SetAsBox( (glassLW/2)/PTM_RATIO, (glassLH/2)/PTM_RATIO );
            glassBodyDef.position.Set( (glassX+(glassLW/2))/PTM_RATIO, (glassY+(glassLH/2))/PTM_RATIO);
            glassBodyDef.userData = new b2AddOn();
            this.world.CreateBody(glassBodyDef).CreateFixture(glassfixDef);
            // right
            glassfixDef.shape = new b2PolygonShape;
            glassfixDef.shape.SetAsBox( (glassRW/2)/PTM_RATIO, (glassRH/2)/PTM_RATIO );
            glassBodyDef.position.Set( (glassX+(glassRW/2)+glassRXOffset)/PTM_RATIO, (glassY+(glassRH/2))/PTM_RATIO);
            glassBodyDef.userData = new b2AddOn();
            this.world.CreateBody(glassBodyDef).CreateFixture(glassfixDef);
            // Sensor (not box2d sensor, juste AABB use to query at frame time)
            var aabb = new b2AABB();
            var off = 32.0;
            aabb.lowerBound.Set( (glassX/PTM_RATIO)+(off/PTM_RATIO), (glassY/PTM_RATIO)+(off/PTM_RATIO) );
            aabb.upperBound.Set( (glassX/PTM_RATIO)+(glassBW/PTM_RATIO)-(off/PTM_RATIO), (glassY/PTM_RATIO)+(glassSensorH/PTM_RATIO) );
            this.waterSensorAABB = aabb;
    },
    
    update:function (dt) {
        if( this.SelectedObject )
        {    
            var b2M = this.b2MousePosition;
            var b2ObjP = this.SelectedObject.GetPosition();
            var speedCoef = 5.0;
            var dir = new b2Vec2( (b2M.x - b2ObjP.x)*speedCoef , (b2M.y - b2ObjP.y)*speedCoef );
            
            this.SelectedObject.SetLinearVelocity(  dir );
        }
        this.TestBoxInWater();

        var velocityIterations = 8;
        var positionIterations = 1;

        this.world.Step(dt, velocityIterations, positionIterations);

        //Iterate over the bodies in the physics world
        for (var b = this.world.GetBodyList(); b; b = b.GetNext()) {
            if (b.GetUserData() != null && b.GetUserData().cocosData != null) {
                var myActor = b.GetUserData().cocosData;
                myActor.setPosition(cc.p(b.GetPosition().x * PTM_RATIO, b.GetPosition().y * PTM_RATIO));
                myActor.setRotation(-1 * cc.RADIANS_TO_DEGREES(b.GetAngle()));
            }
        }
    },
    
    TestBoxInWater:function(){
        
        var __this = this;
        var QueryResultRemain = [];
        var QueryResultNew = [];
        this.world.QueryAABB( function(fix){
            
            if( fix.GetBody().GetUserData().tag == TagList.Box )
            {
                var b = fix.GetBody();
                
                var Find = false;
                for(var i=0;i<__this.BoxInWaterList.length; i++)
                {
                    if( __this.BoxInWaterList[i] == b )
                        Find = true;
                }
                
                if(!Find)
                    QueryResultNew.push(b);
                else
                    QueryResultRemain.push(b);
            }

            return true;
            
        }, this.waterSensorAABB );
        
        
        // remove loop
        for(var i=0;i<__this.BoxInWaterList.length; i++)
        {
            var Find = false;
            for(var j=0;j< QueryResultRemain.length; j++)
            {
                if( this.BoxInWaterList[i] == QueryResultRemain[j] )
                    Find = true;
            }
            
            if(!Find)
            { 
                // Water Effect (Box exit water)
                var contact_point = this.BoxInWaterList[i].GetUserData().cocosData.getPosition();    
                this._water.Height -= 10;
                this.waterSensorAABB.upperBound.y -= (10/PTM_RATIO);
                this._water.addForce(contact_point,200);
            
                // remove a box    
                this.BoxInWaterList[i] = this.BoxInWaterList[ this.BoxInWaterList.length - 1 ];
                this.BoxInWaterList.pop();
                i--;
            }
        }
        
        // add loop
        for(var i=0;i< QueryResultNew.length; i++)
        {
            // Water Effect (Box enter in water)
            var contact_point = QueryResultNew[i].GetUserData().cocosData.getPosition();    
            this._water.Height += 10;
            this.waterSensorAABB.upperBound.y += (10/PTM_RATIO);
            this._water.addForce(contact_point,-200);
            
            this.BoxInWaterList.push( QueryResultNew[i] );
        }
        
        console.log(this.BoxInWaterList.length);
    },
    // inputs 
    onTouchesBegan:function (pTouch, event) {
        if( pTouch[0] )
        {
            var localTouch = this.convertTouchToNodeSpace(pTouch[0]);
            var worldTouch = this.convertToWorldSpace(localTouch);
            var b2WorldPos = transformToB2World(worldTouch);
            this.b2MousePosition = b2WorldPos;
            
            var __this = this;
            this.world.QueryPoint( function(fix){
                if( fix.GetBody().GetUserData().selectable )
                {
                    __this.SelectedObject = fix.GetBody();
                    return false;
                }
                else
                {
                    return true;
                }
            }, b2WorldPos );
        }
    },
    onTouchesMoved:function(pTouch,pEvent){
        // Box2D selectable object
        if( pTouch[0] )
        {
            var localTouch = this.convertTouchToNodeSpace(pTouch[0]);
            var worldTouch = this.convertToWorldSpace(localTouch);
            worldTouch = cc.Director.getInstance().convertToGL(worldTouch);
            var b2WorldPos = new b2Vec2( worldTouch.x / PTM_RATIO, worldTouch.y / PTM_RATIO );
            
            this.b2MousePosition = b2WorldPos;
            
            if( this.SelectedObject )
            {    
                //this.SelectedObject.SetPosition( b2WorldPos/*, this.SelectedObject.GetAngle() */);
                //this.SelectedObject.SetLinearVelocity(  new b2Vec2(0,0) );
            }
        }
    },
    onTouchesEnded:function (pTouch,pEvent){
       if( pTouch[0] )
       {
           // water effects
           this._water.handleTouch( pTouch[0] );
        }
       
       // remove selection
       this.SelectedObject = null;
    },
    onKeyUp:function(e){
    },
    onKeyDown:function(e){
    },
});


var waterScene = cc.Scene.extend({
    onEnter:function () {
        this._super();
        var layer = new _waterScene();
        layer.init();
        this.addChild(layer);
    }
});


